/********************************************************
 Name          : usart_fast.c
 Author        : Khachatur Gyozalyan
 Copyright     : Copyright by Antel Systems
 Description   :
 **********************************************************/
#ifndef USARTFAST_H_
#define USARTFAST_H_

#include "usart_fast.h"
#include "Parameters.h"
#include "delay.h"
#include "pdca.h"
#include "tc_1.h"
//#include "CfgParams.h"

static gpio_map_t USART1_GPIO_MAP =
{
	{USART1_RX_PIN, USART1_RX_FUNCTION},
	{USART1_TX_PIN, USART1_TX_FUNCTION},
	{USART1_RTS_PIN, USART1_RTS_FUNCTION}
};

// USART options.
usart_options_t USART1_OPTIONS =
{
	.baudrate     = 115200,
	.charlength   = 8,
	.paritytype   = USART_NO_PARITY,
	.stopbits     = USART_1_STOPBIT,
	.channelmode  = USART_NORMAL_CHMODE
};

unsigned char usart_buffer[USART_BUFFER_SIZE];
unsigned char usart_out_buffer[USART_BUFFER_SIZE];
//unsigned char usart_out_buffer_temp[2][USART_BUFFER_SIZE/2];
//unsigned int  usart_out_buffer_length[2];
unsigned char data_toglle = 0;
unsigned int  usart_buffer_index = 0,read_index = 0;
unsigned int  usart_out_buffer_index = 0,write_index = 0;

unsigned char flag_rts = 0;

/***************/
//unsigned int temp_buf[2]][10] = {0} ;
/***************/

void dis_rts_pin(void)
{
	USART1->cr = AVR32_USART_CR_RTSDIS_MASK;
}

void en_rts_pin(void)
{
	USART1->cr = AVR32_USART_CR_RTSEN_MASK;
}

void usart_rx_en(void)
{
	USART1->cr = AVR32_USART_CR_RXEN_MASK ;
	/*en_rts_pin();
	flag_rts = 0;*/
}

void terminal_ready(void)
{
	while(USART1->CSR.cts);
}



/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       usart_print_char
  Description :       This function print letter in the terminal.
  Return      :       void
  Parameters  :       char letter which will be print.
  Note        :
\*================================================================================*/
void usart_print_char(char letter)
{
	terminal_ready();
	usart_putchar(USART1,letter);
}

/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       usart_int_handler
  Description :       Handler for USART interrupt.
  Return      :       void
  Parameters  :       void
  Note        :
\*================================================================================*/

#if __GNUC__
__attribute__((__interrupt__))
#elif __ICCAVR32__
__interrupt
#endif
static void usart_int_handler(void)
{
	int c;

	// In the code line below, the interrupt priority level does not need to be
	// explicitly masked as it is already because we are within the interrupt
	// handler.
	// The USART Rx interrupt flag is cleared by side effect when reading the
	// received character.
	// Waiting until the interrupt has actually been cleared is here useless as
	// the call to usart_write_char will take enough time for this before the
	// interrupt handler is leaved and the interrupt priority level is unmasked by
	// the CPU.

	if(usart_read_char(USART1, &c))
	{
		USART1->cr = AVR32_USART_CR_RSTRX_MASK   |
		            AVR32_USART_CR_RSTSTA_MASK  |
		            AVR32_USART_CR_RSTIT_MASK   |
		            AVR32_USART_CR_RSTNACK_MASK ;
		USART1->cr = AVR32_USART_CR_RXEN_MASK |
				AVR32_USART_CR_TXEN_MASK;

	}


	usart_buffer[usart_buffer_index] = c;
	usart_buffer_index++;
	if(usart_buffer_index == USART_BUFFER_SIZE)
		usart_buffer_index = 0;

	if(read_index > usart_buffer_index)
	{
		if((read_index - usart_buffer_index) < USART_BUFFER_AFULL)
		{
			flag_rts = 1;
			dis_rts_pin();
		}
	}
	else
	{
		if(read_index < USART_BUFFER_AFULL)
		{
			if((USART_BUFFER_SIZE - usart_buffer_index + read_index) < USART_BUFFER_AFULL)
			{
				flag_rts = 1;
				dis_rts_pin();
			}
		}
	}
/*	if((read_index == 0) &&  (usart_buffer_index == (USART_BUFFER_SIZE - 100)))
	{
		flag_rts = 1;
		dis_rts_pin();
	}
	else if(usart_buffer_index == read_index - 100)
	{
		flag_rts = 1;
		dis_rts_pin();
	}*/

}
#define PDCA_CHANNEL_USART 0

//unsigned char dma_status = 0;

struct usart_st
{
	unsigned int usart_out_buffer_index;
	unsigned int write_index;
	unsigned int time_start;
	unsigned int time_stop;
};

struct usart_stat
{
	struct usart_st U[256];
	unsigned int index;
};

//int mode;
struct usart_stat Usart_stat;

#if __GNUC__
__attribute__((__interrupt__))
#elif __ICCAVR32__
__interrupt
#endif
static void pdca_int_handler(void)
{
//	if(mode)
		pdca_disable_interrupt_transfer_complete(PDCA_CHANNEL_USART);
//	else
//		pdca_disable_interrupt_reload_counter_zero(PDCA_CHANNEL_USART);

	pdca_disable(PDCA_CHANNEL_USART);
	Usart_stat.U[(Usart_stat.index - 1) & 0xFF].time_stop = TC1_Get_us();
	//dma_status = 0;
}


/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       usart_fast_init
  Description :       This function initializes the USART port settings and USART
                      interrupt.
  Return      :       void
  Parameters  :       void
  Note        :
\*================================================================================*/
void usart_fast_init(void)
{
	// Assign GPIO to USART.
	gpio_enable_module(USART1_GPIO_MAP, sizeof(USART1_GPIO_MAP) / sizeof(USART1_GPIO_MAP[0]));

	// Initialize USART in RS232 mode.
/*
	USART1_OPTIONS.baudrate     = UI_ASYNC_BAUD.val_mat_int[ConfigRoot.OperationSettings.UserInterfaces.Asynch.baud];
	USART1_OPTIONS.charlength   = UI_ASYNC_DATA.val_mat_int[ConfigRoot.OperationSettings.UserInterfaces.Asynch.bits];
*/
	if(ConfigRoot.G_Oper.SG_Data_Interface.param_baud_rate != 230400
	&& ConfigRoot.G_Oper.SG_Data_Interface.param_baud_rate != 115200 && ConfigRoot.G_Oper.SG_Data_Interface.param_baud_rate != 57600
	&& ConfigRoot.G_Oper.SG_Data_Interface.param_baud_rate != 38400 && ConfigRoot.G_Oper.SG_Data_Interface.param_baud_rate != 19200
	&& ConfigRoot.G_Oper.SG_Data_Interface.param_baud_rate != 9600 && ConfigRoot.G_Oper.SG_Data_Interface.param_baud_rate != 4800
	&& ConfigRoot.G_Oper.SG_Data_Interface.param_baud_rate != 2400 && ConfigRoot.G_Oper.SG_Data_Interface.param_baud_rate != 1200)
	{
		ConfigRoot.G_Oper.SG_Data_Interface.param_baud_rate = 115200 ;
		ConfigRoot.G_Oper.SG_Data_Interface.param_data_bits = 8;
		ConfigRoot.G_Oper.SG_Data_Interface.param_parity = 1;
		ConfigRoot.G_Oper.SG_Data_Interface.param_stop_bits = 2;
	}

	if(ConfigRoot.G_Oper.SG_Data_Interface.param_data_bits != 8 &&  ConfigRoot.G_Oper.SG_Data_Interface.param_data_bits != 7 &&
		ConfigRoot.G_Oper.SG_Data_Interface.param_data_bits != 6 && ConfigRoot.G_Oper.SG_Data_Interface.param_data_bits != 5 )
	{
		ConfigRoot.G_Oper.SG_Data_Interface.param_baud_rate = 115200 ;
		ConfigRoot.G_Oper.SG_Data_Interface.param_data_bits = 8;
		ConfigRoot.G_Oper.SG_Data_Interface.param_parity = 1;
		ConfigRoot.G_Oper.SG_Data_Interface.param_stop_bits = 2;
	}

	USART1_OPTIONS.baudrate     = ConfigRoot.G_Oper.SG_Data_Interface.param_baud_rate;
	USART1_OPTIONS.charlength   = ConfigRoot.G_Oper.SG_Data_Interface.param_data_bits;

	switch(ConfigRoot.G_Oper.SG_Data_Interface.param_parity)
	{
	case 1:
		USART1_OPTIONS.paritytype = USART_NO_PARITY;
		break;
	case 2:
		USART1_OPTIONS.paritytype = USART_EVEN_PARITY;
		break;
	case 3:
		USART1_OPTIONS.paritytype = USART_ODD_PARITY;
		break;
	default:
		USART1_OPTIONS.paritytype = USART_NO_PARITY;
		break;
	}

	if(ConfigRoot.G_Oper.SG_Data_Interface.param_stop_bits == 2)
		USART1_OPTIONS.stopbits     = USART_1_STOPBIT;
	else if(ConfigRoot.G_Oper.SG_Data_Interface.param_stop_bits == 3)
		USART1_OPTIONS.stopbits     = USART_1_5_STOPBITS;
	else if(ConfigRoot.G_Oper.SG_Data_Interface.param_stop_bits == 4)
		USART1_OPTIONS.stopbits     = USART_2_STOPBITS;
	else
		USART1_OPTIONS.stopbits     = USART_1_STOPBIT;



	USART1_OPTIONS.channelmode  = USART_NORMAL_CHMODE;
	usart_init_rs232(USART1, &USART1_OPTIONS, FMCK_HZ);
//usart_init_modem(USART1, &USART1_OPTIONS, FMCK_HZ);

	// Disable all interrupts.
	Disable_global_interrupt();
	en_rts_pin();
	// Initialize interrupt vectors.
	//INTC_init_interrupts();

	// Register the USART interrupt handler to the interrupt controller.
	// usart_int_handler is the interrupt handler to register.
	// EXAMPLE_USART_IRQ is the IRQ of the interrupt handler to register.
	// INT0 is the interrupt priority level to assign to the group of this IRQ.
	// void INTC_register_interrupt(__int_handler handler, unsigned int irq, unsigned int int_lev);
	INTC_register_interrupt(&usart_int_handler, USART1_IRQ, INT3);
	// Enable USART Rx interrupt.
	USART1->ier = AVR32_USART_IER_RXRDY_MASK;

	// Register PDCA IRQ interrupt.
	INTC_register_interrupt( (__int_handler) &pdca_int_handler, AVR32_PDCA_IRQ_0, AVR32_INTC_INT1);

	// Enable all interrupts.
	Enable_global_interrupt();
}

/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       usart_set_parameters
  Description :       This function changes the USART port settings.
  Return      :       void
  Parameters  :       void
  Note        :
\*================================================================================*/
void usart_set_parameters(void)
{
	usart_init_rs232(USART1, &USART1_OPTIONS, FMCK_HZ);
	//usart_init_modem(USART1, &USART1_OPTIONS, FMCK_HZ);
	Disable_global_interrupt();
//	en_rts_pin();
	dis_rts_pin();
	flag_rts = 1;
	USART1->ier = AVR32_USART_IER_RXRDY_MASK;
	Enable_global_interrupt();
}

/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       usart_count
  Description :       This function return number of bytes in usart_buffer.
  Return      :       int - Number of bytes copied.
  Parameters  :       unsigned char *data
                      int size
  Note        :
\*================================================================================*/
int usart_count(void)
{
  int count = usart_buffer_index - read_index;
  if (count < 0)
    count += USART_BUFFER_SIZE;
  return count;
}

/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       usart_get_data
  Description :       This function copy data from usart_buffer to data.
  Return      :       unsigned int - Number of bytes copied.
  Parameters  :       unsigned char *data
                      int size
  Note        :
\*================================================================================*/
unsigned int usart_get_data(unsigned char *data,unsigned int size)
{

	int l_size = usart_count();
	if(!l_size)
	{
		if(flag_rts)
		{
			flag_rts = 0;
			en_rts_pin();
		}
		return 0;
	}
	if (size > l_size)
		size = l_size;

	l_size = size;

	if (read_index + l_size >= USART_BUFFER_SIZE)
	{
		int count;
		count = USART_BUFFER_SIZE - read_index;
		memcpy(data,&usart_buffer[read_index],count);
		l_size -= count;
		read_index = 0;
		memcpy(&data[count],usart_buffer,l_size);
	}
	else
		memcpy(data,&usart_buffer[read_index],l_size);
	if(flag_rts)
	{
		if((USART_BUFFER_SIZE - usart_count()) > USART_BUFFER_AFULL)
		{
			flag_rts = 0;
			en_rts_pin();
		}
	}
	read_index += l_size;
	return size;
}

/*================================================================================*\
  Author      :       Khachatur Gyozalyan
  ================================================================================
  Function    :       usart_put_data
  Description :       This function send data buffer to USART port.
  Return      :       void
  Parameters  :       unsigned char *data
                      int size
  Note        :
\*================================================================================*/
/*void usart_put_data(unsigned char * data,unsigned int size)
{
	memcpy(&usart_out_buffer_temp[data_toglle][0],data,size);
	usart_out_buffer_length[data_toglle] = size;
	data_toglle ^= 1;
}*/
unsigned int usart_out_data_count1 = 0;

void usart_put_data(unsigned char * data,unsigned int size)
{
	unsigned int l_size = size;
	usart_out_data_count1 += size;
	if (write_index + l_size >= USART_BUFFER_SIZE)
	{
		int count;
		count = USART_BUFFER_SIZE - write_index;
		memcpy(&usart_out_buffer[write_index],data,count);
		l_size -= count;
		write_index = 0;
		memcpy(usart_out_buffer,&data[count],l_size);
	}
	else
		memcpy(&usart_out_buffer[write_index],data,l_size);

	write_index += l_size;
}

//! The channel instance for the USART example, here PDCA channel 0 (highest priority).


/*! \brief Init interrupt controller and register pdca_int_handler interrupt.
 */
void pdca_set_irq(void)
{
#if __GNUC__
  // Disable all interrupt/exception.
  Disable_global_interrupt();

//  INTC_init_interrupts();

  // Register the compare interrupt handler to the interrupt controller
  // and enable the compare interrupt.
  // (__int_handler) &pdca_int_handler The handler function to register.
  // AVR32_PDCA_IRQ_0 The interrupt line to register to.
  // AVR32_INTC_INT0  The priority level to set for this interrupt line.
  // INTC_register_interrupt(__int_handler handler, int line, int priority);
  INTC_register_interrupt( (__int_handler) &pdca_int_handler, AVR32_PDCA_IRQ_0, AVR32_INTC_INT1);
#endif

  // Enable all interrupt/exception.
  Enable_global_interrupt();
}

unsigned int usart_out_data_count = 0;

void usart_output(void)
{
	// PDCA channel options
	static pdca_channel_options_t PDCA_OPTIONS;

	int current_index = write_index;
	if(current_index == usart_out_buffer_index)
		return;
	PDCA_OPTIONS.addr = NULL;                             // memory address
	PDCA_OPTIONS.pid = AVR32_PDCA_PID_USART1_TX;          // select peripheral - data are transmit on USART TX line.
	PDCA_OPTIONS.size = 0;                                // transfer counter
	PDCA_OPTIONS.r_addr = NULL;                           // next memory address
	PDCA_OPTIONS.r_size = 0;                              // next transfer counter
	PDCA_OPTIONS.transfer_size = PDCA_TRANSFER_SIZE_BYTE; // select size of the transfer
/*	if(dma_status)
	{
		dma_status = 2;
		return;
	}*/
	if(pdca_get_channel_status(PDCA_CHANNEL_USART))
		return;

	Usart_stat.U[Usart_stat.index & 0xFF].usart_out_buffer_index = usart_out_buffer_index;
	Usart_stat.U[Usart_stat.index & 0xFF].write_index = write_index;
	Usart_stat.U[Usart_stat.index & 0xFF].time_start = TC1_Get_us();
	Usart_stat.index++;


//	dma_status = 1;
/*	if(current_index > usart_out_buffer_index)
	{
		PDCA_OPTIONS.addr = &usart_out_buffer[usart_out_buffer_index];
		PDCA_OPTIONS.size = current_index - usart_out_buffer_index;
	//	mode = 0;
	}
	else
	{
		PDCA_OPTIONS.addr = &usart_out_buffer[usart_out_buffer_index];
		PDCA_OPTIONS.size = USART_BUFFER_SIZE - usart_out_buffer_index;
	//	PDCA_OPTIONS.r_addr = &usart_out_buffer[0];
	//	PDCA_OPTIONS.r_size = current_index;
	//	mode = 1;
	}*/
	PDCA_OPTIONS.addr = &usart_out_buffer[usart_out_buffer_index];
	if(current_index > usart_out_buffer_index)
	{
		PDCA_OPTIONS.size = current_index - usart_out_buffer_index;
		usart_out_buffer_index = current_index;
	}
	else
	{
		PDCA_OPTIONS.size = USART_BUFFER_SIZE - usart_out_buffer_index;
		PDCA_OPTIONS.r_addr = &usart_out_buffer[0];
		PDCA_OPTIONS.r_size = current_index;
		usart_out_buffer_index = current_index;
//		usart_out_buffer_index = 0;
	}
	usart_out_data_count += (PDCA_OPTIONS.size + PDCA_OPTIONS.r_size);


	// Init PDCA channel with the pdca_options.
	pdca_init_channel(PDCA_CHANNEL_USART, &PDCA_OPTIONS); // init PDCA channel with options.

	// Enable pdca interrupt each time the reload counter reaches zero, i.e. each time
	// half of the ASCII animation (either anim1 or anim2) is transferred.
//	if(mode)
		pdca_enable_interrupt_transfer_complete(PDCA_CHANNEL_USART);
//	else
//		pdca_enable_interrupt_reload_counter_zero(PDCA_CHANNEL_USART);

	// Enable now the transfer.
	pdca_enable(PDCA_CHANNEL_USART);
//	usart_out_buffer_index = current_index;
}

void print_uart_info(void)
{
	printk("uart out data count %d\r\n" , usart_out_data_count);
	printk("uart in data count %d\r\n" , usart_out_data_count1);
	usart_out_data_count1 = 0 ;
	usart_out_data_count = 0 ;
}

#endif //USARTFAST_H_
